using System;
using System.Collections.Generic;

namespace InfiniTec.Data
{
	/// <summary>
	/// Represents a single-valued property
	/// </summary>
	/// <typeparam name="T">The type of the property</typeparam>
	public class Property<T> : Property, IPropertyInfo, IEquatable<Property<T>>
	{
		private T _Value;

		/// <summary>
		/// Creates a new instance of this class using the specified namespace and local
		/// name.
		/// </summary>
		/// <param name="value">The value of the property</param>
		/// <param name="propertyDefinition">The property definition for this instance.</param>
		public Property(PropertyDefinition<T> propertyDefinition, T value)
			: base(propertyDefinition)
		{
			Value = value;
			SetChanged();
		}

		/// <summary>
		/// Creates a new instance of this class using the specified namespace and local
		/// name.
		/// </summary>
		/// <param name="propertyDefinition">The property definition for this instance.</param>
		public Property(PropertyDefinition<T> propertyDefinition)
			: base(propertyDefinition)
		{
		}

		/// <summary>
		/// Gets or sets the value of the item
		/// </summary>
		public T Value
		{
			get { return _Value; }
			set
			{
				if (EqualityComparer<T>.Default.Equals(_Value, value))
				{
					return;
				}
				_Value = value;
				SetChanged();
			}
		}

		public PropertyDefinition<T> Definition
		{
			get { return (PropertyDefinition<T>)((IPropertyInfo)this).PropertyDefinition; }
		}

		bool IPropertyInfo.PropertyValueIsNull
		{
			get
			{
				if (typeof(T).IsClass) return ReferenceEquals(Value, null);
				return false;
			}
		}

		object IPropertyInfo.Value
		{
			get { return Value; }
		}

		/// <summary>
		/// Clones this instance.
		/// </summary>
		/// <returns></returns>
		public override Property Clone()
		{
			var result = new Property<T>(Definition, Value) { Status = Status };
			if (HasChanged)
			{
				result.SetChanged();
			}
			else
			{
				result.ResetChanged();
			}


			return result;
		}

		public static bool operator ==(Property<T> left, Property<T> right)
		{
			return Equals(left, right);
		}

		public static bool operator !=(Property<T> left, Property<T> right)
		{
			return !Equals(left, right);
		}

		public bool Equals(Property<T> property)
		{
			if (ReferenceEquals(null, property)) return false;
			if (ReferenceEquals(this, property)) return true;
			return base.Equals(property) && Equals(property._Value, _Value);
		}

		public override bool Equals(object obj)
		{
			if (ReferenceEquals(null, obj)) return false;
			if (ReferenceEquals(this, obj)) return true;
			return Equals(obj as Property<T>);
		}

		public override int GetHashCode()
		{
			unchecked
			{
				{
					return (base.GetHashCode() * 397) ^ _Value.GetHashCode();
				}
			}
		}
	}
}